////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016-2018 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file  camxjpegaggrnode.cpp
/// @brief JPEG aggregator node class implementation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/// @todo (CAMX-1988) Code clean up apply NC001 coding standard on entire project
/// @todo (CAMX-1989) Code clean up apply GR030 coding standard on entire project

#include "camxcsl.h"
#include "camxmem.h"
#include "camxutils.h"
#include "camxtrace.h"
#include "camxhwdefs.h"
#include "camxhwcontext.h"
#include "camxpropertyblob.h"
#include "camxjpegaggrnode.h"
#include "camxcsljumptable.h"
#include "camxcslresourcedefs.h"
#include "camxhal3module.h"

CAMX_NAMESPACE_BEGIN

static const UINT MaxJPEGAggrOutput             = 1;                        ///< Max output ports
static const UINT MaxJPEGAggrInput              = 2;                        ///< Max input ports

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// JPEGAggrNode::Create
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
JPEGAggrNode* JPEGAggrNode::Create(
    const NodeCreateInputData* pCreateInputData,
    NodeCreateOutputData*      pCreateOutputData)
{
    CAMX_UNREFERENCED_PARAM(pCreateInputData);
    CAMX_UNREFERENCED_PARAM(pCreateOutputData);

    return CAMX_NEW JPEGAggrNode;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// JPEGAggrNode::Destroy
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID JPEGAggrNode::Destroy()
{
    CAMX_DELETE this;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// JPEGAggrNode::JPEGAggrNode
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
JPEGAggrNode::JPEGAggrNode()
    : m_pEXIFParams(NULL)
    , m_pEXIFComposer(NULL)
{
    m_pNodeName = "JPEG Aggregator";
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// JPEGAggrNode::~JPEGAggrNode
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
JPEGAggrNode::~JPEGAggrNode()
{
    if (NULL != m_pEXIFParams)
    {
        CAMX_DELETE m_pEXIFParams;
        m_pEXIFParams = NULL;
    }

    if (NULL != m_pEXIFComposer)
    {
        CAMX_DELETE m_pEXIFComposer;
        m_pEXIFComposer = NULL;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// JPEGAggrNode::GetMaxJpegSize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID JPEGAggrNode::GetMaxJpegSize(
    const NodeCreateInputData* pCreateInputData)
{
    CamxResult  result = CamxResultSuccess;
    m_maxjpegsize      = 0;

    UINT32          propertyCount   = pCreateInputData->pNodeInfo->nodePropertyCount;

    for (UINT32 count = 0; count < propertyCount; count++)
    {
        if (NodePropertyStitchMaxJpegSize == pCreateInputData->pNodeInfo->pNodeProperties[count].id)
        {
            m_maxjpegsize =
                atoi(static_cast<const CHAR*>(pCreateInputData->pNodeInfo->pNodeProperties[count].pValue));
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// JPEGAggrNode::ProcessingNodeInitialize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult JPEGAggrNode::ProcessingNodeInitialize(
    const NodeCreateInputData* pCreateInputData,
    NodeCreateOutputData*      pCreateOutputData)
{
    CAMX_UNREFERENCED_PARAM(pCreateInputData);

    CamxResult result                = CamxResultSuccess;
    INT32      deviceIndex           = -1;
    UINT       indicesLengthRequired = 0;

    CAMX_ASSERT_MESSAGE(JPEGAggregator == Type(), "Node type not JPEGAggregator");

    if ((NULL == pCreateInputData) || (NULL == pCreateOutputData))
    {
        CAMX_LOG_ERROR(CamxLogGroupJPEG, "NULL params pCreateInputData %p pCreateOutputData %p", pCreateInputData,
            pCreateOutputData);
        result = CamxResultEInvalidPointer;
        return result;
    }
    GetMaxJpegSize(pCreateInputData);
    if (CamxResultSuccess == result)
    {
        pCreateOutputData->maxOutputPorts = MaxJPEGAggrOutput;
        pCreateOutputData->maxInputPorts  = MaxJPEGAggrInput;

        /// Add JPEG encoder device indices
        result = HwEnvironment::GetInstance()->GetDeviceIndices(CSLDeviceTypeJPEGE, &deviceIndex, 1, &indicesLengthRequired);
    }

    if (CamxResultSuccess == result)
    {
        result = AddDeviceIndex(deviceIndex);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// JPEGAggrNode::PostPipelineCreate
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult JPEGAggrNode::PostPipelineCreate()
{
    CamxResult result = CamxResultSuccess;

    m_pEXIFParams = CAMX_NEW JPEGEXIFParams();
    if (NULL == m_pEXIFParams)
    {
        result = CamxResultENoMemory;
        CAMX_LOG_ERROR(CamxLogGroupJPEG, "Memory allocation for EXIF params failed");
    }

    if (CamxResultSuccess == result)
    {
        m_pEXIFComposer = CAMX_NEW JPEGEXIFComposer();
        if (NULL == m_pEXIFComposer)
        {
            result = CamxResultENoMemory;
            CAMX_LOG_ERROR(CamxLogGroupJPEG, "Memory allocation for EXIF composer failed");
        }
    }

    if (CamxResultSuccess == result)
    {
        result = m_pEXIFParams->Initialize(this, IsRealTime());
    }

    if (CamxResultSuccess == result)
    {
        result = m_pEXIFComposer->Initialize();
    }

    if (CamxResultSuccess != result)
    {
        if (NULL != m_pEXIFParams)
        {
            CAMX_DELETE m_pEXIFParams;
            m_pEXIFParams = NULL;
        }

        if (NULL != m_pEXIFComposer)
        {
            CAMX_DELETE m_pEXIFComposer;
            m_pEXIFComposer = NULL;
        }
    }

    // Determine if rotation is handled before JPEG encoding
    m_rotationHandled = GetStaticSettings()->overrideGPURotationUsecase;

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// JPEGAggrNode::ProcessingNodeFinalizeInputRequirement
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult JPEGAggrNode::ProcessingNodeFinalizeInputRequirement(
    BufferNegotiationData* pBufferNegotiationData)
{
    CAMX_ASSERT(NULL != pBufferNegotiationData);

    CamxResult  result                     = CamxResultSuccess;
    UINT        optimalInputWidth          = 0;
    UINT        optimalInputHeight         = 0;
    UINT        perOutputPortOptimalWidth  = 0;
    UINT        perOutputPortOptimalHeight = 0;

    if (NULL == pBufferNegotiationData)
    {
        CAMX_LOG_ERROR(CamxLogGroupJPEG, "NULL params pBufferNegotiationData");
        result = CamxResultEInvalidPointer;
    }

    if (CamxResultSuccess == result)
    {
        // The JPEG node will have to loop through all the output ports which are connected to a child node or a HAL target.
        for (UINT outputIndex = 0; outputIndex < pBufferNegotiationData->numOutputPortsNotified; outputIndex++)
        {
            OutputPortNegotiationData* pOutputPortNegotiationData =
                &pBufferNegotiationData->pOutputPortNegotiationData[outputIndex];

            perOutputPortOptimalWidth = 0;
            perOutputPortOptimalHeight = 0;

            Utils::Memset(&pOutputPortNegotiationData->outputBufferRequirementOptions, 0, sizeof(BufferRequirement));

            // Go through the requirements of the input ports connected to the output port
            for (UINT inputIndex = 0; inputIndex < pOutputPortNegotiationData->numInputPortsNotification; inputIndex++)
            {
                BufferRequirement* pInputPortRequirement = &pOutputPortNegotiationData->inputPortRequirement[inputIndex];

                if (perOutputPortOptimalWidth < pInputPortRequirement->optimalWidth)
                {
                    perOutputPortOptimalWidth = pInputPortRequirement->optimalWidth;
                }

                if (perOutputPortOptimalHeight < pInputPortRequirement->optimalHeight)
                {
                    perOutputPortOptimalHeight = pInputPortRequirement->optimalHeight;
                }
            }

            /// Store the buffer requirements for this output port which will be reused to set, during forward walk.
            /// The values stored here could be final output dimensions unless it is overridden by forward walk.
            pOutputPortNegotiationData->outputBufferRequirementOptions.optimalWidth  = perOutputPortOptimalWidth;
            pOutputPortNegotiationData->outputBufferRequirementOptions.optimalHeight = perOutputPortOptimalHeight;
            pOutputPortNegotiationData->outputBufferRequirementOptions.minWidth      = perOutputPortOptimalWidth;
            pOutputPortNegotiationData->outputBufferRequirementOptions.maxWidth      = perOutputPortOptimalWidth;
            pOutputPortNegotiationData->outputBufferRequirementOptions.minHeight     = perOutputPortOptimalHeight;
            pOutputPortNegotiationData->outputBufferRequirementOptions.maxHeight     = perOutputPortOptimalHeight;

            CAMX_LOG_INFO(CamxLogGroupJPEG,
                          "JPEG Aggr Optimal Output Dim, W:%d x H:%d!\n",
                          perOutputPortOptimalWidth,
                          perOutputPortOptimalHeight);

            if (optimalInputWidth < perOutputPortOptimalWidth)
            {
                optimalInputWidth = perOutputPortOptimalWidth;
            }

            if (optimalInputHeight < perOutputPortOptimalHeight)
            {
                optimalInputHeight = perOutputPortOptimalHeight;
            }
        }

        if ((0 == optimalInputWidth) || (0 == optimalInputHeight))
        {
            result = CamxResultEFailed;

            CAMX_LOG_ERROR(CamxLogGroupJPEG,
                           "JPEG Aggr Buffer Negotiation Failed, W:%d x H:%d!\n",
                           optimalInputWidth,
                           optimalInputHeight);
        }
        else
        {
            CAMX_LOG_INFO(CamxLogGroupJPEG,
                          "JPEG Aggr Optimal Input Dim, W:%d x H:%d!\n",
                          optimalInputWidth,
                          optimalInputHeight);

            UINT32 numInputPorts = 0;
            UINT32 inputPortId[MaxJPEGAggrInput];

            // Get Input Port List
            GetAllInputPortIds(&numInputPorts, &inputPortId[0]);

            pBufferNegotiationData->numInputPorts = numInputPorts;

            for (UINT input = 0; input < numInputPorts; input++)
            {
                pBufferNegotiationData->inputBufferOptions[input].nodeId     = Type();
                pBufferNegotiationData->inputBufferOptions[input].instanceId = InstanceID();
                pBufferNegotiationData->inputBufferOptions[input].portId     = inputPortId[input];

                BufferRequirement* pInputBufferRequirement =
                    &pBufferNegotiationData->inputBufferOptions[input].bufferRequirement;

                pInputBufferRequirement->optimalWidth   = optimalInputWidth;
                pInputBufferRequirement->optimalHeight  = optimalInputHeight;
                pInputBufferRequirement->minWidth       = optimalInputWidth;
                pInputBufferRequirement->minHeight      = optimalInputHeight;
                pInputBufferRequirement->maxWidth       = optimalInputWidth;
                pInputBufferRequirement->maxHeight      = optimalInputHeight;
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// JPEGAggrNode::GetThumbnailFormat
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult JPEGAggrNode::GetThumbnailFormat(
    ImageFormat* pThumbnailFormat)
{
    CamxResult          result              = CamxResultSuccess;
    UINT                jpegTag[1]          = { 0 };
    VOID*               pData[1]            = { 0 };
    UINT                length              = CAMX_ARRAY_SIZE(jpegTag);
    UINT64              jpegDataOffset[1]   = { 0 };
    DimensionCap*       pThumbnailDim       = NULL;

    jpegTag[0] = InputJPEGThumbnailSize;

    result = GetDataList(jpegTag, pData, jpegDataOffset, length);
    if (NULL == pThumbnailFormat)
    {
        CAMX_LOG_ERROR(CamxLogGroupJPEG, "JPEG Thumb dims NULL");
        result = CamxResultEInvalidPointer;
    }

    if (CamxResultSuccess == result)
    {
        pThumbnailDim = reinterpret_cast<DimensionCap*>(pData[0]);
        if (NULL != pThumbnailDim)
        {
            CAMX_LOG_INFO(CamxLogGroupJPEG, "JPEG Thumb dims %d %d", pThumbnailDim->width, pThumbnailDim->height);
            pThumbnailFormat->width  = pThumbnailDim->width;
            pThumbnailFormat->height = pThumbnailDim->height;
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupJPEG, "JPEG Thumb dims NULL");
        }
    }
    else
    {
        CAMX_LOG_WARN(CamxLogGroupJPEG, "JPEG THumb Dims not available");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// JPEGAggrNode::CheckIfThumbNeeded
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL JPEGAggrNode::CheckIfThumbNeeded(
    ExecuteProcessRequestData* pExecuteProcessRequestData)
{
    CamxResult  result          = CamxResultSuccess;
    ImageFormat thumbnailFormat = {};
    BOOL        thumbnailNeeded = TRUE;

    if (pExecuteProcessRequestData->pEnabledPortsInfo->numInputPorts < MaxJPEGAggrInput)
    {
        thumbnailNeeded = FALSE;
    }
    else
    {
        result = GetThumbnailFormat(&thumbnailFormat);

        if ((0 == thumbnailFormat.width) || (0 == thumbnailFormat.height) || (CamxResultSuccess != result))
        {
            thumbnailNeeded = FALSE;
        }
        else
        {
            thumbnailNeeded = TRUE;
        }
    }

    return thumbnailNeeded;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// JPEGAggrNode::ExecuteProcessRequest
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult JPEGAggrNode::ExecuteProcessRequest(
    ExecuteProcessRequestData* pExecuteProcessRequestData)
{
    CamxResult                result             = CamxResultSuccess;
    NodeProcessRequestData*   pNodeRequestData   = NULL;
    PerRequestActivePorts*    pPerRequestPorts   = NULL;
    PerRequestOutputPortInfo* pOutputPort        = NULL;
    ImageBuffer*              pOutputImageBuffer = NULL;
    CaptureRequest*           pCaptureRequest    = NULL;
    BOOL                      bThumbnailNeeded   = TRUE;

    CAMX_ENTRYEXIT(CamxLogGroupJPEG);

    if (NULL == pExecuteProcessRequestData)
    {
        CAMX_LOG_ERROR(CamxLogGroupJPEG, "NULL params pExecuteProcessRequestData %p", pExecuteProcessRequestData);
        result = CamxResultEInvalidPointer;
    }

    if (CamxResultSuccess == result)
    {
        pNodeRequestData = pExecuteProcessRequestData->pNodeProcessRequestData;
        pPerRequestPorts = pExecuteProcessRequestData->pEnabledPortsInfo;

        if ((NULL == pNodeRequestData) || (NULL == pPerRequestPorts))
        {
            CAMX_LOG_ERROR(CamxLogGroupJPEG, "NULL params pNodeRequestData %p pPerRequestPorts %p", pNodeRequestData,
                           pPerRequestPorts);

            result = CamxResultEInvalidPointer;
        }
    }
    INT sequenceNumber = 0;
    if (CamxResultSuccess == result)
    {
        pCaptureRequest = pNodeRequestData->pCaptureRequest;
        sequenceNumber  = pNodeRequestData->processSequenceId;

        if (NULL == pCaptureRequest)
        {
            CAMX_LOG_ERROR(CamxLogGroupJPEG, "NULL params pCaptureRequest %p", pCaptureRequest);
            result = CamxResultEInvalidPointer;
        }
    }

    if ((CamxResultSuccess == result) && (0 == sequenceNumber))
    {
        SetDependencies(pNodeRequestData, pPerRequestPorts);
    }

    if ((CamxResultSuccess == result) && (1 == sequenceNumber))
    {
        static const UINT PropertiesJpeg[] = { PropertyIDJPEGEncodeOutInfo };
        VOID* pJPEGEncodeOutInfoData[1] = { 0 };
        UINT length = CAMX_ARRAY_SIZE(PropertiesJpeg);
        UINT64 propertyDataJpegOffset[1] = { 0 };

        GetDataList(PropertiesJpeg, pJPEGEncodeOutInfoData, propertyDataJpegOffset, length);
        m_encodeOutParams = *reinterpret_cast<EncoderOutInfo*>(pJPEGEncodeOutInfoData[0]);
        m_encodeOutParams.bufferFilledSize = *(m_encodeOutParams.pBufferFilledSize);

        if (TRUE == m_rotationHandled)
        {
            // Get the GPU rotation metadata
            UINT32 metaTag = 0;
            result = VendorTagManager::QueryVendorTagLocation("com.qti.node.gpu", "FrameDimension", &metaTag);

            if (CamxResultSuccess == result)
            {
                static const UINT PropertiesGPURotation[]          = { metaTag };
                VOID*             pGPURotationData[1]              = { 0 };
                UINT64            propertyDataGPURotationOffset[1] = { 0 };

                length = CAMX_ARRAY_SIZE(PropertiesGPURotation);

                GetDataList(PropertiesGPURotation, pGPURotationData, propertyDataGPURotationOffset, length);
                m_rotatedDimensions[0] = reinterpret_cast<UINT32*>(pGPURotationData[0])[0];
                m_rotatedDimensions[1] = reinterpret_cast<UINT32*>(pGPURotationData[0])[1];
            }
        }

        if ((CamxResultSuccess == result) && (m_encodeOutParams.bufferFilledSize <= 0))
        {
            CAMX_LOG_ERROR(CamxLogGroupJPEG, "Invalid encoded buffer size! Main Image Encoded size %d",
                m_encodeOutParams.bufferFilledSize);
            result = CamxResultEOutOfBounds;
        }

        bThumbnailNeeded = CheckIfThumbNeeded(pExecuteProcessRequestData);

        if ((TRUE == bThumbnailNeeded) && (CamxResultSuccess == result))
        {
            static const UINT PropertiesJpegThumb[] ={ PropertyIDJPEGEncodeOutInfoThumbnail };
            VOID* pJPEGEncodeOutInfoThumbnailData[1] = { 0 };
            length = CAMX_ARRAY_SIZE(PropertiesJpegThumb);
            UINT64 JPEGEncodeOutInfoThumbnailOffset[1] ={ 0 };

            GetDataList(PropertiesJpegThumb, pJPEGEncodeOutInfoThumbnailData, JPEGEncodeOutInfoThumbnailOffset, length);
            if (NULL != pJPEGEncodeOutInfoThumbnailData[0])
            {
                m_encodeOutParamsThumbnail = *reinterpret_cast<EncoderOutInfo*>(pJPEGEncodeOutInfoThumbnailData[0]);
                m_encodeOutParamsThumbnail.bufferFilledSize = *(m_encodeOutParamsThumbnail.pBufferFilledSize);
                if (m_encodeOutParamsThumbnail.bufferFilledSize <= 0)
                {
                    CAMX_LOG_ERROR(CamxLogGroupJPEG, "Invalid encoded buffer size! Thumbnail Encoded size %d",
                        m_encodeOutParamsThumbnail.bufferFilledSize);
                    result = CamxResultEOutOfBounds;
                }
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupJPEG, "failed thumb encoder OutInfo");
                result = CamxResultEInvalidArg;
            }
        }

        CAMX_LOG_INFO(CamxLogGroupJPEG, "Encoded size Main %d @ %p Thumb %d @ %p",
            m_encodeOutParams.bufferFilledSize,
            m_encodeOutParams.pBufferFilledSize,
            m_encodeOutParamsThumbnail.bufferFilledSize,
            m_encodeOutParamsThumbnail.pBufferFilledSize);

        if (CamxResultSuccess == result)
        {
            m_pEXIFParams->SetEncodeOutParams(&m_encodeOutParams);
            m_pEXIFParams->SetEncodeOutParamsThumbnail(&m_encodeOutParamsThumbnail);

            for (UINT i = 0; i < pPerRequestPorts->numInputPorts; i++)
            {
                PerRequestInputPortInfo* pInputPort = &pPerRequestPorts->pInputPorts[i];

                if ((NULL != pInputPort) && (NULL != pInputPort->pImageBuffer))
                {
                    if (pInputPort->portId == 0) /* Main Image */
                    {
                        ImageFormat imageFormat;
                        if (NULL != pInputPort->pImageBuffer->GetFormat())
                        {
                            imageFormat = *pInputPort->pImageBuffer->GetFormat();
                            if (TRUE == m_rotationHandled)
                            {
                                imageFormat.width    = m_rotatedDimensions[0];
                                imageFormat.height   = m_rotatedDimensions[1];
                                imageFormat.rotation = Rotation::CW0Degrees;
                            }
                            result = m_pEXIFParams->SetEXIFImageParams(imageFormat);
                        }
                        m_pEXIFComposer->AddInputMainBuf(pInputPort->pImageBuffer);
                    }
                    else if (pInputPort->portId == 1)  /* Thumbnail Image */
                    {
                        if (TRUE == bThumbnailNeeded)
                        {
                            if (NULL != pInputPort->pImageBuffer->GetFormat())
                            {
                                ImageFormat thumbnailFormat = *pInputPort->pImageBuffer->GetFormat();;
                                result = GetThumbnailFormat(&thumbnailFormat);
                                CAMX_LOG_VERBOSE(CamxLogGroupJPEG,
                                    "pi %d JPEG Aggr reporting Input config portid %d width %d height %d fence %d req %d",
                                    i,
                                    pInputPort->portId,
                                    thumbnailFormat.width,
                                    thumbnailFormat.height,
                                    *pInputPort->phFence,
                                    pNodeRequestData->pCaptureRequest->requestId);

                                if ((CamxResultSuccess == result) &&
                                    (0 != thumbnailFormat.width) &&
                                    (0 != thumbnailFormat.height))
                                {
                                    m_pEXIFComposer->AddInputThumbBuffer(pInputPort->pImageBuffer);
                                    result = m_pEXIFParams->SetEXIFImageParamsThumb(thumbnailFormat);
                                }
                            }
                            else
                            {
                                CAMX_LOG_ERROR(CamxLogGroupJPEG, "%s: format is Null ", __FUNCTION__);
                                result = CamxResultEInvalidArg;
                            }
                        }
                    }
                    else
                    {
                        CAMX_LOG_ERROR(CamxLogGroupJPEG, "%s: Unknown Input Port", __FUNCTION__);
                    }
                }
                else
                {
                    CAMX_LOG_ERROR(CamxLogGroupJPEG, "%s: Input Port/Image Buffer is Null ", __FUNCTION__);

                    result = CamxResultEInvalidArg;
                }

                if (CamxResultSuccess != result)
                {
                    CAMX_LOG_ERROR(CamxLogGroupJPEG, "Input Port: Add IO config failed i %d", i);
                    break;
                }
            }
        }

        if ((CamxResultSuccess == result) && (pPerRequestPorts->numOutputPorts == 1))
        {
            pOutputPort = &pPerRequestPorts->pOutputPorts[0];

            if (NULL != pOutputPort)
            {
                pOutputImageBuffer = pOutputPort->pImageBuffer[0];
                CAMX_LOG_INFO(CamxLogGroupJPEG,
                              "JPEG Aggr reporting I/O config, portId=%d, hFence=%d, request=%llu",
                              pOutputPort->portId,
                              *(pOutputPort->phFence),
                              pNodeRequestData->pCaptureRequest->requestId);
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupJPEG, "%s: Output Port is Null ", __FUNCTION__);
                result = CamxResultEInvalidArg;
            }

            if (NULL != pOutputImageBuffer)
            {
                m_pEXIFComposer->AddOutputBuf(pOutputImageBuffer);
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupJPEG, "%s: Output Image is Null ", __FUNCTION__);
                result = CamxResultEInvalidArg;
            }

            if (CamxResultSuccess != result)
            {
                CAMX_LOG_ERROR(CamxLogGroupJPEG, "Output Port: Add IO config failed");
            }
        }

        if (CamxResultSuccess == result)
        {
            const StaticSettings* pStaticSettings    = HwEnvironment::GetInstance()->GetStaticSettings();
            UINT propertiesDebugDataJpeg[] = { 0 };
            if (TRUE == IsRealTime())
            {
                propertiesDebugDataJpeg[0] = PropertyIDDebugDataAll;
            }
            else
            {
                UINT metaTag = 0;
                VendorTagManager::QueryVendorTagLocation("org.quic.camera.debugdata", "DebugDataAll", &metaTag);
                propertiesDebugDataJpeg[0] = metaTag | InputMetadataSectionMask;
            }

            static const UINT Length = CAMX_ARRAY_SIZE(propertiesDebugDataJpeg);
            VOID* pPropertyDebugDataJpegData[Length] = { 0 };
            UINT64 propertyDebugDataJpegOffset[Length] = { 0 };
            GetDataList(propertiesDebugDataJpeg, pPropertyDebugDataJpegData, propertyDebugDataJpegOffset, Length);

            if (NULL != pPropertyDebugDataJpegData[0])
            {
                DebugData* pDebugData = reinterpret_cast<DebugData*>(pPropertyDebugDataJpegData[0]);

                m_pEXIFComposer->SetStatsDebugData(reinterpret_cast<UINT8*>(pDebugData->pData),
                                                    static_cast<UINT32>(pDebugData->size),
                                                    pStaticSettings->debugDataHeaderString);
            }
            else
            {
                CAMX_LOG_INFO(CamxLogGroupJPEG, "DebugDataAll: property not available for writing debug data.");
            }

            // 3A debug data failures are non-fatal
            result = CamxResultSuccess;
        }

        if (CamxResultSuccess == result)
        {
            m_pEXIFParams->GetEXIFData();

            m_pEXIFComposer->SetParams(m_pEXIFParams);
            result = m_pEXIFComposer->StartComposition();
        }

        if (CamxResultSuccess == result)
        {
            m_pEXIFParams->SetEXIFVendorTags();
        }

        if ((CamxResultSuccess == result) && (NULL != pOutputPort))
        {
            result = CSLFenceSignal(*pOutputPort->phFence, CSLFenceResultSuccess);
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupJPEG, "JPEG aggregartion failed!!");
            if (NULL != pOutputPort)
            {
                result = CSLFenceSignal(*pOutputPort->phFence, CSLFenceResultFailed);
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// JPEGAggrNode::SetPropertyDependency
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID JPEGAggrNode::SetPropertyDependency(
    NodeProcessRequestData*  pNodeRequestData,
    PerRequestActivePorts*    pEnabledPorts)
{
    CamxResult  result = CamxResultSuccess;
    UINT32 count = 0;
    ImageFormat thumbnailFormat;
    BOOL bThumbnailNeeded = TRUE;

    pNodeRequestData->dependencyInfo[0].dependencyFlags.hasPropertyDependency = TRUE;
    pNodeRequestData->dependencyInfo[0].processSequenceId                     = 1;
    pNodeRequestData->dependencyInfo[0].propertyDependency.properties[count]  = PropertyIDJPEGEncodeOutInfo;

    count++;

    if (pEnabledPorts->numInputPorts < MaxJPEGAggrInput)
    {
        bThumbnailNeeded = FALSE;
    }
    else
    {
        result = GetThumbnailFormat(&thumbnailFormat);

        if ((0 == thumbnailFormat.width) || (0 == thumbnailFormat.height) || (CamxResultSuccess != result))
        {
            bThumbnailNeeded = FALSE;
        }
        else
        {
            bThumbnailNeeded = TRUE;
        }

    }
    if (TRUE == bThumbnailNeeded)
    {
        pNodeRequestData->dependencyInfo[0].dependencyFlags.hasPropertyDependency = TRUE;
        pNodeRequestData->dependencyInfo[0].propertyDependency.properties[count] = PropertyIDJPEGEncodeOutInfoThumbnail;
        count++;
    }

    if (m_rotationHandled == TRUE)
    {
        UINT32 metaTag = 0;
        VendorTagManager::QueryVendorTagLocation("com.qti.node.gpu", "FrameDimension", &metaTag);
        pNodeRequestData->dependencyInfo[0].dependencyFlags.hasPropertyDependency = TRUE;
        pNodeRequestData->dependencyInfo[0].propertyDependency.properties[count] = metaTag;

        count++;
    }

    pNodeRequestData->dependencyInfo[0].propertyDependency.count = count;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// JPEGAggrNode::SetBufferDependency
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID JPEGAggrNode::SetBufferDependency(
    NodeProcessRequestData*   pNodeRequestData,
    PerRequestActivePorts*    pEnabledPorts)
{
    UINT fencesNotSignalled = 0;

    for (UINT i = 0; i < pEnabledPorts->numInputPorts; i++)
    {
        PerRequestInputPortInfo* pPort = &pEnabledPorts->pInputPorts[i];

        if (NULL != pPort)
        {
            pNodeRequestData->dependencyInfo[0].bufferDependency.phFences[fencesNotSignalled] = pPort->phFence;
            pNodeRequestData->dependencyInfo[0].bufferDependency.pIsFenceSignaled[fencesNotSignalled] = pPort->pIsFenceSignaled;
            pNodeRequestData->dependencyInfo[0].dependencyFlags.hasBufferDependency = TRUE;
            pNodeRequestData->dependencyInfo[0].processSequenceId                   = 1;

            fencesNotSignalled++;
        }
    }
    pNodeRequestData->dependencyInfo[0].bufferDependency.fenceCount = fencesNotSignalled;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// JPEGAggrNode::SetDependencies
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID JPEGAggrNode::SetDependencies(
    NodeProcessRequestData*   pNodeRequestData,
    PerRequestActivePorts*    pEnabledPorts)
{
    // First set all the buffer dependencies
    SetBufferDependency(pNodeRequestData, pEnabledPorts);
    // Set the property dependencies are satisfied
    SetPropertyDependency(pNodeRequestData, pEnabledPorts);

    if (0 != pNodeRequestData->dependencyInfo[0].dependencyFlags.dependencyFlagsMask)
    {
        pNodeRequestData->numDependencyLists = 1;
    }
}

CAMX_NAMESPACE_END
